package com.jetpackframework.room;


import androidx.annotation.NonNull;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;

import com.blankj.utilcode.util.GsonUtils;
import com.jetpackframework.ContextUtil;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;

/**
 * 该类只是针对以下升级方案使用生效
 *      1.添加某一列
 *      2.删除或创建一张表
 *
 *      3.删除某一列由于sqlite数据库不支持删除操作  故无法实现
 */
public class DatabaseMigration extends Migration {

    public DatabaseMigration(int startVersion, int endVersion) {
        super(startVersion, endVersion);
    }

    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        try {
            List<EntitiesBean> oldSchema = parserDatabaseSchema(startVersion);
            List<EntitiesBean> newSchema = parserDatabaseSchema(endVersion);
            List<String> sqls = genSql(oldSchema,newSchema);
            for (String sql : sqls){
                database.execSQL(sql);
            }
        }catch (IOException e){
            e.printStackTrace();
        }

    }

    private List<String> genSql(List<EntitiesBean> oldSchema, List<EntitiesBean> newSchema) {
        List<String> sqls = new ArrayList<>();
        //删除一张表
        for (EntitiesBean old : oldSchema){
            if (!newSchema.contains(old)){
                sqls.add("DROP TABLE IF EXISTS "+old.tableName);
            }
        }
        //创建一张表
        for (EntitiesBean old : newSchema){
            if (!oldSchema.contains(old)){
                sqls.add(old.createSql.replace("${TABLE_NAME}",old.tableName));
            }
        }
        //添加某一列
        for (EntitiesBean oldEntity : oldSchema){
            for (EntitiesBean newEntity : newSchema){
                if (oldEntity.tableName.equals(newEntity.tableName)){
                    for (FieldsBean fieldsBean : newEntity.fields){
                        if (!oldEntity.fields.contains(fieldsBean)){
                            sqls.add("alter table "+oldEntity.tableName +" add "+fieldsBean.columnName+" "+fieldsBean.affinity);
                        }
                    }
                    break;
                }
            }
        }
        return sqls;
    }

    private List<EntitiesBean> parserDatabaseSchema(int version) throws IOException {
        InputStreamReader reader = new InputStreamReader(ContextUtil.get().getAssets().open(version+".json"));
        DatabaseSchema json = GsonUtils.fromJson(reader, DatabaseSchema.class);
        return json.database.entities;
    }


    public static class DatabaseSchema implements Serializable {
        public int formatVersion;
        public DatabaseBean database;

    }
    public static class DatabaseBean{
        public int version;
        public String identityHash;
        public List<EntitiesBean> entities;
        public List<String> setupQueries;
    }
    public static class EntitiesBean {
        public String tableName;
        public String createSql;
        public PrimaryKeyBean primaryKey;
        public List<FieldsBean> fields;
        public List<Index> indices;
        public List<ForeignKey> foreignKeys;

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            EntitiesBean that = (EntitiesBean) o;
            return Objects.equals(tableName, that.tableName);
        }

        @Override
        public int hashCode() {
            return Objects.hash(tableName);
        }
    }
    public static class PrimaryKeyBean {
        public boolean autoGenerate;
        public List<String> columnNames;
    }
    public static class FieldsBean {
        public String fieldPath;
        public String columnName;
        public String affinity;
        public boolean notNull;

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            FieldsBean that = (FieldsBean) o;
            return Objects.equals(columnName, that.columnName);
        }

        @Override
        public int hashCode() {
            return Objects.hash(columnName);
        }
    }
    public static class Index{
        public String name;
        public boolean unique;
        public List<String> columnNames;
        public String createSql;
    }
    public static class ForeignKey{
        public String table;
        public String onDelete;
        public String onUpdate;
        public List<String> columns;
        public List<String> referencedColumns;
    }
}
